package cool.taomu.toolkit.service.kademlia.provider

import com.google.common.collect.Lists
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import com.google.inject.Inject
import com.google.inject.Provider
import com.google.inject.name.Named
import cool.taomu.toolkit.rpc.thrift.entity.ThriftConfig
import cool.taomu.toolkit.service.kademlia.entity.Constant
import cool.taomu.toolkit.service.kademlia.entity.KPeerNode
import cool.taomu.toolkit.utils.FileTools
import java.io.File
import java.math.BigInteger
import java.nio.file.Files
import java.nio.file.StandardOpenOption
import java.util.ArrayList
import java.util.List
import java.util.WeakHashMap
import java.util.concurrent.ArrayBlockingQueue
import org.apache.commons.io.FileUtils
import org.apache.commons.io.IOUtils
import org.slf4j.LoggerFactory
import org.apache.commons.lang3.exception.ExceptionUtils

class KBucketsProvider implements Provider<List<ArrayBlockingQueue<KPeerNode>>> {
    val static LOG = LoggerFactory.getLogger(KBucketsProvider)
    @Inject
    List<ThriftConfig> config;

    @Inject
    @Named("taomu.kademlia.name")
    String name;

    @Inject(optional=true)
    @Named("taomu.kademlia.k")
    int k = 8;

    List<ArrayBlockingQueue<KPeerNode>> buckets = new ArrayList(160)

    override get() {
        LOG.info("加载已有节点数据")
        var server = config
        val cache = new WeakHashMap<String, ThriftConfig>();
        server.forEach [ c |
            cache.put(c.name, c)
        ]
        var info = cache.get(name);
        if (info !== null) {
            for (var i = 0; i < 160; i++) {
                buckets.add(new ArrayBlockingQueue<KPeerNode>(this.k));
            }
            val localFileName = #[info.host, info.port, "local", "json"].join(".")
            var nodeInfoFile = new File(Constant.TORRENT_DOWNLOAD_PATH + File.separator + localFileName);
            var jsonStr = FileUtils.readFileToString(nodeInfoFile, "UTF-8")
            val localNode = new Gson().fromJson(jsonStr, KPeerNode)
            FileTools.scanner(Constant.TORRENT_DOWNLOAD_PATH, null, [
                try {
                    LOG.debug(it.fileName.toFile.name)
                    if (!it.fileName.toFile.name.equals(localFileName) &&
                        it.fileName.toFile.name.endsWith(".local.json")) {
                        try(var input = Files.newInputStream(it,StandardOpenOption.READ)) {
                            var content = IOUtils.toString(input, "UTF-8")
                            val node = new Gson().fromJson(content, KPeerNode)
                            // 获取节点所属的桶索引
                            var int bucketIndex = getBucketIndex(localNode.getId(), node.getId());
                            // 获取桶索引对应的队列
                            val queue = buckets.get(bucketIndex);
                            queue.add(node);
                        }
                    }
                } catch (JsonSyntaxException e) {
                    LOG.error(ExceptionUtils.getStackTrace(e))
                }
            ])
        }
        LOG.info("加载到桶中的数据条数:{}", buckets.stream().mapToInt[it.size].sum())
        buckets
    }

    // 获取节点所属的桶索引
    def int getBucketIndex(String id1, String id2) {
        return xorDistance(id1, id2).bitCount;
    }

    // 带缓存的异或距离方法
    def BigInteger xorDistance(String id1, String id2) {
        return computeXor(id1, id2)
    }

    // 构建 key 并排序，保证 (a,b) 和 (b,a) 是同一个 key
    private def List<String> key(String id1, String id2) {
        if (id1.compareTo(id2 === null ? "" : id2) <= 0) {
            return Lists.newArrayList(id1, id2)
        } else {
            return Lists.newArrayList(id2, id1)
        }
    }

    // 实际计算逻辑（只有缓存未命中时才执行）
    private def BigInteger computeXor(String id1, String id2) {
        val bigInt1 = new BigInteger(id1, 2)
        val bigInt2 = new BigInteger(id2, 2)
        return bigInt1.xor(bigInt2)
    }

}
